using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using CoreCalc.SheetDefinedFunctions;

namespace CoreCalc.GUI
{
  public partial class SDF : Form
  {
    private WorkbookForm gui;
    private Workbook wb;

    public SDF(WorkbookForm gui, Workbook wb)
    {
      InitializeComponent();
      this.gui = gui;
      this.wb = wb;
      tipLabel.Text =
          "Tip: Doubleclick on the input / output list boxes" + Environment.NewLine +
          "to select the cells in the workbook" + Environment.NewLine +
          "Tip2: Use the CTRL button to do complex" + Environment.NewLine +
          "selections. Selection order == input" + Environment.NewLine +
          "parameter order";

      PopulateFunctionListBox(false);
    }

    public void PopulateFunctionListBox(bool rememberSelectedIndex)
    {
      int selectedIndex = functionsListBox.SelectedIndex;
      string selectedName = "";
      if (selectedIndex != -1)
        selectedName = ((SdfInfo)functionsListBox.Items[selectedIndex]).name;

      functionsListBox.Items.Clear();
      int i = 0;
      foreach (SdfInfo info in SdfManager.GetAllInfos())
      {
        functionsListBox.Items.Add(info);
        if (selectedName == info.name)
          selectedIndex = i;
        i++;
      }
      if (rememberSelectedIndex && selectedIndex != -1) 
        functionsListBox.SelectedIndex = selectedIndex;
    }

    public void PopulateFunctionListBox(string name)
    {
      for (int i = 0; i < functionsListBox.Items.Count; i++)
      {
        if (((SdfInfo)functionsListBox.Items[i]).name.ToLower() == name.ToLower())
        {
          functionsListBox.SelectedIndex = i;
        }
      }
    }

    private void functionsListbox_SelectedIndexChanged(object sender, EventArgs e)
    {
      RefreshInfo();
    }

    private void RefreshInfo()
    {
      ClearInfo();
      if (functionsListBox.SelectedItem != null)
      {
        SdfInfo info = (SdfInfo)functionsListBox.SelectedItem;
        foreach (FullCellAddr cell in info.inputCells)
        {
          inputCellsListBox.Items.Add(cell);
          gui.ChangeCellBackgroundColor(cell, Color.LightGreen);
        }
        outputCellTextBox.Text = info.outputCell.ToString();
        gui.ChangeCellBackgroundColor(info.outputCell, Color.LightSkyBlue);
        ConstructFormulaExample();
      }
    }

    private void ConstructFormulaExample()
    {
      SdfInfo info = (SdfInfo)functionsListBox.SelectedItem;
      StringBuilder sb = new StringBuilder();
      sb.Append(info.name);
      sb.Append("(");
      foreach (FullCellAddr cell in info.inputCells)
      {
        sb.Append(cell.ca + ", ");
      }
      if (info.Arity > 0)
        sb.Remove(sb.Length - 2, 2);
      sb.Append(")");
      exampleFormulaCallLabel.Text = sb.ToString();
    }

    public void ClearInfo()
    {
      inputCellsListBox.Items.Clear();
      outputCellTextBox.Text = string.Empty;
      gui.UnselectAllCells();
      gui.ResetAllCellBackgroundColors();
      exampleFormulaCallLabel.Text = "";
      errorLabel.Text = "";
    }

    private void functionsListBox_KeyDown(object sender, KeyEventArgs e)
    {
      if (e.KeyValue == (int)Keys.Delete && functionsListBox.SelectedItem != null)
      {
        DeleteSDF();
      }
    }

    private void DeleteSDF()
    {
      if (functionsListBox.SelectedItem != null)
      {
        SdfInfo info = (SdfInfo)functionsListBox.SelectedItem;
        SdfManager.DeleteMethod(info.name);
        PopulateFunctionListBox(false);
        ClearInfo();
      }
    }

    private void button1_Click(object sender, EventArgs e)
    {
      AddFunction nameBox = new AddFunction(wb, this);
      nameBox.ShowDialog();
    }

    private void button2_Click(object sender, EventArgs e)
    {
      DeleteSDF();
    }

    private void SetErrorMsg(string s)
    {
      errorLabel.Text = s;
      MessageBox.Show(s);
      Console.WriteLine(s);
    }

    private void addInputCellsButton_Click(object sender, EventArgs e)
    {
      if (functionsListBox.SelectedItem == null)
        SetErrorMsg("Must select a function first");
      else
      {
        string[] temp = outputCellTextBox.Text.Split('!');
        FullCellAddr output = new FullCellAddr(temp[1], wb[temp[0]]);
        if (!IsValidSelectedSheetTab())
          SetErrorMsg("Input parameters must be defined inside" + Environment.NewLine +
                      "the function sheet where the function " + Environment.NewLine +
                      "was originally defined");
        else UpdateFunction(gui.GetSelectedCells(), output);
      }
    }

    private void UpdateFunction(List<FullCellAddr> input, FullCellAddr output)
    {
      UpdateFunction(input.ToArray(), output);
    }

    private void UpdateFunction(FullCellAddr[] input, FullCellAddr output)
    {
      if (functionsListBox.SelectedItem != null)
      {
        SdfInfo info = (SdfInfo)functionsListBox.SelectedItem;
        SdfManager.DeleteMethod(info.name);
        SdfManager.CreateFunction(info.name, output, input);
        PopulateFunctionListBox(true);
        RefreshInfo();
      }
    }

    private void inputCellsListBox_DoubleClick(object sender, EventArgs e)
    {
      if (functionsListBox.SelectedItem != null)
      {
        SdfInfo info = (SdfInfo)functionsListBox.SelectedItem;
        gui.UnselectAllCells();
        gui.SelectCellsProgrammatically(info.inputCells);
      }
    }

    private void addOutputCellsButton_Click(object sender, EventArgs e)
    {
      if (functionsListBox.SelectedItem == null) SetErrorMsg("Must select a function first");
      else
      {
        List<FullCellAddr> list = gui.GetSelectedCells();
        if (list.Count != 1) SetErrorMsg("There must exactly one output cell");
        else if (!IsValidSelectedSheetTab())
          SetErrorMsg("Output parameter must be defined inside" + Environment.NewLine +
                      "the function sheet where the function " + Environment.NewLine +
                      "was originally defined");
        else
        {
          SdfInfo info = (SdfInfo)functionsListBox.SelectedItem;
          UpdateFunction(info.inputCells, gui.GetSelectedCells()[0]);
        }
      }
    }

    private void outputCellTextBox_MouseDoubleClick(object sender, MouseEventArgs e)
    {
      if (functionsListBox.SelectedItem != null)
      {
        SdfInfo info = (SdfInfo)functionsListBox.SelectedItem;
        gui.UnselectAllCells();
        gui.SelectCellsProgrammatically(info.outputCell);
      }
    }

    private bool IsValidSelectedSheetTab()
    {
      SdfInfo info = (SdfInfo)functionsListBox.SelectedItem;
      return info.outputCell.sheet.Name == gui.GetSelectedSheet().Name;
    }
  }
}